﻿using System;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Nvigorate.Relational.Mapping;
using Nvigorate.Relational.Mapping.Index;
using Nvigorate.Relational.Mapping.Interface;
using Nvigorate.TestObjects;

namespace Nvigorate.Test.Relational.Binding
{
    [TestClass]
    public class MapIndexTests
    {
        #region Fixture Methods

        [TestInitialize]
        public void Initialize()
        {
            new MapIndexFactory().ResetMapIndex();
        }

        #endregion

        #region Initialize Tests

        /// <summary>
        /// Integration Test
        /// </summary>
        [TestMethod, Ignore]
        public void Initialize_UseAppConfiguration_PopulatesCollection()
        {
            var mapIndex = new MapIndexFactory().CreateMapIndex();
            Assert.IsNotNull(mapIndex["Employee", typeof (Employee)]);
        }

        #endregion

        #region AddBindingMap Tests

        [TestMethod]
        public void AddBindingMap_ValidMap_AddsToCollection()
        {
            // Arrange
            var bindingMap = new BindingMap {RepositoryName = "Table1", TargetType = typeof (Employee)};

            // Act
            var actual = new MapIndex();
            actual.AddBindingMap(bindingMap);

            // Assert
            Assert.AreEqual(bindingMap.RepositoryName, actual["Table1", typeof (Employee)].RepositoryName);
        }

        #endregion

        #region AddBindingMaps Tests

        [TestMethod]
        public void AddBindingMaps_SingleValidMap_AddsToCollection()
        {
            // Arrange
            var bindingMap = new BindingMap {RepositoryName = "Table1", TargetType = typeof (Employee)};

            // Act
            var actual = CreateMapIndex();
            actual.AddBindingMaps(new[] {bindingMap});

            // Assert
            var map = actual["Table1", typeof(Employee)];
            Assert.IsNotNull(map);
            Assert.AreEqual(bindingMap.RepositoryName, map.RepositoryName);
        }

        [TestMethod]
        public void AddBindingMaps_MultipleValidMapsSameDataSource_AddsToCollection()
        {
            // Arrange
            var bindingMap1 = new BindingMap
                                  {
                                      RepositoryName = "Table1",
                                      TargetType = typeof (Employee),
                                      TargetName = "ClassA"
                                  };
            var bindingMap2 = new BindingMap
                                  {
                                      RepositoryName = "Table1",
                                      TargetType = typeof (Address),
                                      TargetName = "ClassB"
                                  };

            // Act
            var actual = CreateMapIndex();
            actual.AddBindingMaps(new[] {bindingMap1, bindingMap2});

            // Assert
            var map1 = actual["Table1", typeof(Employee)];
            var map2 = actual["Table1", typeof(Address)];
            Assert.AreEqual(bindingMap1.TargetName, map1.TargetName);
            Assert.AreEqual(bindingMap2.TargetName, map2.TargetName);
        }

        [TestMethod]
        public void AddBindingMaps_MultipleValidMapsDifferentDataSource_AddsToCollection()
        {
            // Arrange
            var bindingMap1 = new BindingMap
                                  {
                                      RepositoryName = "Table1",
                                      TargetType = typeof (Employee),
                                      TargetName = "ClassA"
                                  };
            var bindingMap2 = new BindingMap
                                  {
                                      RepositoryName = "Table2",
                                      TargetType = typeof (Address),
                                      TargetName = "ClassB"
                                  };

            // Act
            var actual = CreateMapIndex();
            actual.AddBindingMaps(new[] {bindingMap1, bindingMap2});

            // Assert
            Assert.IsNotNull(actual["Table1", typeof(Employee)]);
            Assert.IsNotNull(actual["Table2", typeof(Address)]);
            Assert.AreEqual(bindingMap1.TargetName, actual["Table1", typeof (Employee)].TargetName);
            Assert.AreEqual(bindingMap2.TargetName, actual["Table2", typeof (Address)].TargetName);
        }

        #endregion

        #region HasBindingMap Tests

        [TestMethod]
        public void HasBindingMap_NoMatchingSourceOrType_ReturnsFalse()
        {
            var actual = CreateMapIndex();
            Assert.IsFalse(actual.HasBindingMap(typeof (Employee), "Table1"));
        }

        [TestMethod]
        public void HasBindingMap_TypeMatchesButSourceDoesNot_ReturnsFalse()
        {
            var bindingMap = new BindingMap {RepositoryName = "Table1", TargetType = typeof (Employee)};
            var actual = CreateMapIndex();
            actual.AddBindingMap(bindingMap);

            Assert.IsFalse(actual.HasBindingMap(typeof (Employee), "Table2"));
        }

        [TestMethod]
        public void HasBindingMap_SourceMatchesButTypeDoesNot_ReturnsFalse()
        {
            var bindingMap = new BindingMap {RepositoryName = "Table1", TargetType = typeof (Employee)};
            var actual = CreateMapIndex();
            actual.AddBindingMap(bindingMap);

            Assert.IsFalse(actual.HasBindingMap(typeof (Address), "Table1"));
        }

        [TestMethod]
        public void HasBindingMap_MatchFound_ReturnsTrue()
        {
            var bindingMap = new BindingMap {RepositoryName = "Table1", TargetType = typeof (Employee)};
            var actual = CreateMapIndex();
            actual.AddBindingMap(bindingMap);

            Assert.IsTrue(actual.HasBindingMap(typeof (Employee), "Table1"));
        }

        #endregion

        #region Indexer Tests

        [TestMethod]
        public void Indexer_NoSourceProvided_CreatesDefaultMap()
        {
            var mapIndex = CreateMapIndex();
            var actual = mapIndex["", typeof (TestType)];

            Assert.IsNotNull(actual);
            var assignments = actual.GetAssignments().Cast<IAssignment>().ToList();
            Assert.AreEqual(2, assignments.Count);
            Assert.IsNotNull(
                assignments.FirstOrDefault(a => a.Source.Equals(new AssignmentPoint("TestType", "StringField"))));
            Assert.IsNotNull(
                assignments.FirstOrDefault(a => a.Target.Equals(new AssignmentPoint(
                    typeof(TestType).FullName, "StringField"))));
            Assert.IsNotNull(
                assignments.FirstOrDefault(a => a.Source.Equals(new AssignmentPoint("TestType", "DateTimeField"))));
            Assert.IsNotNull(
                assignments.FirstOrDefault(a => a.Target.Equals(new AssignmentPoint(
                    typeof(TestType).FullName, "DateTimeField"))));
        }

        #endregion

        #region Private Methods

        private IMapIndex CreateMapIndex()
        {
            var mapIndex = new MapIndex();
            mapIndex.ClearMaps();
            return mapIndex;
        }

        #endregion
        #region Nested Types

        public class TestType
        {
            public string StringField { get; set; }
            public DateTime DateTimeField { get; set; }
        }

        #endregion
    }
}